#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define GPIOLED_CNT     1           /*设备号个数*/
#define GPIOLED_NAME    "my-leds"   /*名字*/
#define SYSLEDOFF       0           /*关灯 sysled*/
#define SYSLEDON        1           /*开灯 sysled*/
#define LEDOFF          2           /*led open*/
#define LEDON           3           /*led close*/

struct gpioled_dev
{
    dev_t devid;            //设备号
    struct cdev cdev;       //cdev
    struct class *class;    //类
    struct device *device;  //设备
    int major;              //主设备号
    int minor;              //次设备号
    struct device_node *nd; //设备节点
   // int led_gpio;           //sysled所使用的GPIO编号
    int led1_gpio;          //外接的led gpio编号
};

struct gpioled_dev gpioled;

static int led_open(struct inode *inode,struct file *filp)
{
    filp->private_data = &gpioled;
    return 0;
}

static ssize_t led_read(struct file *filp,char __user *buf,size_t cnt,loff_t *offt)
{
    return 0;
}

static ssize_t led_write(struct file *filp,const char __user *buf,size_t cnt,loff_t *offt)
{
    int rv;
    unsigned char databuf[1];
    unsigned char ledstate;
    struct gpioled_dev *dev = filp->private_data;

    rv = copy_from_user(databuf,buf,cnt);       //接收用户发来的信息
    if(rv<0)
    {
        printk("kernel write failure!\r\n");
        return -EFAULT;
    }

    ledstate = databuf[0];  //获取状态值

/*    if(ledstate == SYSLEDON)
    {
        gpio_set_value(dev->led_gpio,1);
    }
    else if(ledstate == SYSLEDOFF)
    {
        gpio_set_value(dev->led_gpio,0);
    }*/
    if(ledstate == LEDON)
    {
        gpio_set_value(dev->led1_gpio,1);
    }
    else if(ledstate == LEDOFF)
    {
        gpio_set_value(dev->led1_gpio,0);
    }
    return 0;
}

static int led_release(struct inode *inode,struct file *filp)
{
    return 0;
}

static struct file_operations gpioled_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,
};

static int __init led_init(void)
{
    int rv = 0;
    const char *str;

    /*设置LED所使用的GPIO
    1.获取设备节点：gpioled*/
    gpioled.nd = of_find_node_by_path("/my_leds");//位置是设备树中的位置
    if(gpioled.nd == NULL)
    {
        printk("gpioled node not find!\r\n");
        return -EINVAL;
    }

    /*2.读取status属性*/
    rv = of_property_read_string(gpioled.nd,"status",&str);
    if(rv<0)
        return -EINVAL;

    if(strcmp(str,"okay"))
        return -EINVAL;

    /*3.获取compatible属性值进行匹配*/
    rv = of_property_read_string(gpioled.nd,"compatible",&str);
    if(rv<0)
    {
        printk("gpioled :Failed to get compatible property\r\n");
        return -EINVAL;
    }
    if(strcmp(str,"my-leds"))
    {
        printk("gpioled:Compatible match failed %s\r\n",str);
        return -EINVAL;
    }

    /*4.获取设备树中的gpio属性，得到LED所使用的LED编号*/
 /*   gpioled.led_gpio = of_get_named_gpio(of_find_node_by_path("/my_leds/led1"),"gpios",0);
    if(gpioled.led_gpio<0)
    {
        printk("can't get sysled gpios");
        return -EINVAL;
    }
	*/
    gpioled.led1_gpio = of_get_named_gpio(of_find_node_by_path("/my_leds/led0"),"gpios",0);
    if(gpioled.led1_gpio<0)
    {
        printk("can't get led0 gpios");
        return -EINVAL;
    }
 //   printk("led-gpio num = %d\r\n",gpioled.led_gpio);
    printk("led-gpio num = %d\r\n",gpioled.led1_gpio);

    /*5.向GPIO子系统申请使用GPIO*/
 /*   rv = gpio_request(gpioled.led_gpio,"LED-GPIO");
    if(rv)
    {
        printk(KERN_ERR "gpioled:Failed to request led-gpio\r\n");
        return rv;
    }*/
    rv = gpio_request(gpioled.led1_gpio,"LED1-GPIO");
    if(rv)
    {
        printk(KERN_ERR "gpioled1:Failed to request led-gpio\r\n");
        return rv;
    }

    /*6.设置GPIO输出，并且输出低电平，默认关闭LED灯*/
  /*  rv = gpio_direction_output(gpioled.led_gpio,0);
    if(rv<0)
    {
        printk("can't set gpio! \r\n");
    }
*/
    rv = gpio_direction_output(gpioled.led1_gpio,0);
    if(rv<0)
    {
        printk("can't set gpio! \r\n");
    }
	gpioled.major=0;
	gpioled.minor=0;

    /*注册字符设备驱动*/
    /*1.创建设备号*/
    if(gpioled.major)
    {
        gpioled.devid = MKDEV(gpioled.devid,0);
        rv = register_chrdev_region(gpioled.devid,GPIOLED_CNT,GPIOLED_NAME);
        if(rv<0)
        {
            pr_err("can't register %s char driver [ret=%d]\n",GPIOLED_NAME,GPIOLED_CNT);
            goto free_gpio;
        }
    }
    else
    {
        rv = alloc_chrdev_region(&gpioled.devid,0,GPIOLED_CNT,GPIOLED_NAME);
        if(rv < 0)
        {
            pr_err("%s Can't alloc_chrdev_region,ret = %d\r\n",GPIOLED_NAME,rv);
            goto free_gpio;
        }
        gpioled.major = MAJOR(gpioled.devid); //获取分配号的主设备号
        gpioled.minor = MINOR(gpioled.devid); //获取分配号的次设备号
    }
    printk("gpioled major = %d,minor = %d\r\n",gpioled.major,gpioled.minor);

    /*2初始化cdev*/
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev,&gpioled_fops);

    /*3.添加一个cdev*/
    rv = cdev_add(&gpioled.cdev,gpioled.devid,GPIOLED_CNT);
    if(rv<0)
    {
        goto del_unregister;
    }

    /*4.创建类*/
    gpioled.class = class_create(THIS_MODULE,GPIOLED_NAME);
    if(IS_ERR(gpioled.class))
    {
        goto del_cdev;
    }

    /*创建设备*/
    gpioled.device = device_create(gpioled.class,NULL,gpioled.devid,NULL,GPIOLED_NAME);
    if(IS_ERR(gpioled.device))
    {
        goto destroy_class;
    }
    return 0;

destroy_class:
    class_destroy(gpioled.class); 
del_cdev:
    cdev_del(&gpioled.cdev);
del_unregister:
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
free_gpio:
//    gpio_free(gpioled.led_gpio);
    gpio_free(gpioled.led1_gpio);
    return -EIO;
}

static void __exit led_exit(void)
{
    /*注销字符设备驱动*/
    cdev_del(&gpioled.cdev);
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
    device_destroy(gpioled.class,gpioled.devid);
    class_destroy(gpioled.class);
  //  gpio_free(gpioled.led_gpio);
    gpio_free(gpioled.led1_gpio);
}

module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Dai rongan");
